package net.gdface.service.search;

import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import net.gdface.service.client.SaveImage;
import net.gdface.utils.Assert;
import net.gdface.worker.QueueManager;
import net.gdface.worker.WorkData;
import net.gdface.worker.WorkDataWithURI;

public class SaveWorker  implements Runnable{
	static final Logger logger = LoggerFactory.getLogger(SaveWorker.class);
	private static SaveImage saveProcessor;
	private static QueueManager<WorkDataWithURI> queueManager;
	private static long timeoutMills = 2000;

	/**
	 * 重试策略
	 * @author guyadong
	 *
	 */
	public static interface RetryStrategy {
		static final String VAR_SAVERETRY = "saveretry";
		/**当前工作对象状态是不是重试
		 * @param woc
		 * @return
		 */
		public boolean isRetry(WorkData woc);
		/**
		 * 当前任务对象是不是需要重试,任务失败时调用
		 * @param woc
		 * @param e 抛出的异常,可能为{@code null}
		 * @return 为{@code true}需要重试,否则就结束任务
		 */
		public boolean needRetry(WorkData woc, Throwable e);
		/**
		 * 增加重试计数,当重试时调用
		 * @param woc
		 */
		public void incCount(WorkData woc);
	}
	/**
	 * (默认)重试一次策略
	 */
	public static final RetryStrategy RETRY_STRATEGY_DEFAULT=new RetryStrategy(){
		@Override
		public boolean isRetry(WorkData woc) {
			return Boolean.TRUE == woc.getVariables(VAR_SAVERETRY);
		}
		@Override
		public boolean needRetry(WorkData woc, Throwable e) {
			return Boolean.TRUE != woc.getVariables(VAR_SAVERETRY);
		}
		@Override
		public void incCount(WorkData woc) {
			woc.setVariables(VAR_SAVERETRY, true);
		}		
	};
	/**
	 * 重试固定次数的策略
	 * @author guyadong
	 *
	 */
	public static class FixCountRetryStrategy implements RetryStrategy {
		/**
		 * (默认)重试一次策略
		 */
		public static final RetryStrategy RETRY_DEFAULT=new FixCountRetryStrategy(1);
		/**
		 * 不重试策略
		 */
		public static final RetryStrategy NO_RETRY=new FixCountRetryStrategy(0);
		/**
		 *无限重试策略
		 */
		public static final RetryStrategy INFINITE_RETRY=new FixCountRetryStrategy(Integer.MAX_VALUE);
		private final int maxRetryCount;
		/**
		 * @param maxRetryCount 最大重试次数 <=0时则不重试
		 */
		protected FixCountRetryStrategy(int maxRetryCount) {
			this.maxRetryCount = maxRetryCount;
		}

		@Override
		public boolean isRetry(WorkData woc) {
			 return null!=woc.getVariables(VAR_SAVERETRY);
		}

		@Override
		public boolean needRetry(WorkData woc, Throwable e) {
			Integer c=woc.getVariables(VAR_SAVERETRY);
			return maxRetryCount>0&&(null==c||c<maxRetryCount);
		}

		@Override
		public void incCount(WorkData woc) {
			Integer c = woc.getVariables(VAR_SAVERETRY);
			woc.setVariables(VAR_SAVERETRY, null == c?1:c+1);
		}		
	};
	/**
	 * 重试策略对象<br>
	 */
	private static RetryStrategy retryStrategy=RETRY_STRATEGY_DEFAULT;
	/**
	 * 设置运行所需要的参数
	 * @param queueManager 队列管理器用于管理任务队列
	 * @param saveProcessor   存储处理器用于数据保存
	 * @param retryStrategy     重试策略对象,当任务出错时决定是否重试以及重试次数,为{@code null}时使用默认值 {@link #RETRY_STRATEGY_DEFAULT}
	 * @param checkIntervalMills 任务队列检查间隔时间(毫秒)
	 */
	@SuppressWarnings("unchecked")
	public static void set(QueueManager<? extends WorkDataWithURI> queueManager, SaveImage saveProcessor, RetryStrategy retryStrategy, long checkIntervalMills) {
		Assert.notNull(queueManager, "queueManager");
		Assert.notNull(saveProcessor, "saveProcessor");
		SaveWorker.queueManager = (QueueManager<WorkDataWithURI>) queueManager;
		SaveWorker.saveProcessor = saveProcessor;
		if(null!=retryStrategy)
			SaveWorker.retryStrategy=retryStrategy;
		if (checkIntervalMills > 0)
			SaveWorker.timeoutMills = checkIntervalMills;
	}

	public SaveWorker() {
		super();
	}

	@Override
	public void run() {
		WorkDataWithURI woc = null;
		try {
			// 从队列中取出数据，超时结束
			while (null != (woc = queueManager.pop(timeoutMills, TimeUnit.MILLISECONDS))) {
				boolean finished = false;
				try {
					// saveRetry==true重试的时候，不用再执行needSave判断一次
					if (retryStrategy.isRetry(woc) || saveProcessor.needSave(woc)) {
						saveProcessor.save(woc);
					}
					finished = true;
				} catch (RuntimeException e) {
					saveProcessor.onRuntimeException(e, woc);
					if (retryStrategy.needRetry(woc, e)) {
						// 出错重试
						retryStrategy.incCount(woc);
						queueManager.push(woc, 10L * 1000);
						logger.info("{} {} RETRY:{}", this.getClass().getSimpleName(), e.getClass().getSimpleName(),woc.getURI());
					} else
						finished = true;
				} finally {
					onFinally(woc,finished);
				}
			}
		} catch (RuntimeException e) {
			// 未知的异常
			logger.error(e.getMessage(), e);
		} catch (InterruptedException e) {
			Thread.currentThread().interrupt();
		}/*finally{
			this.onFinally(woc, true);
		}*/
	}

	private void onFinally(WorkDataWithURI woc, boolean finished) {
		if (null!=woc&&finished) {
			saveProcessor.recordStatus(woc);
			afterRecordStatus(woc);
		}
	}

	protected void afterRecordStatus(WorkDataWithURI woc) {
	}
	
}